﻿#include <algorithm>
#include <gtest/gtest.h>
#include <iostream>
#include <learn/utils/output.h>
#include <learn/utils/sfinae.h>
#include <map>
#include <string>
#include <vector>

using namespace xi;
using namespace xi::utils;

enum class Color
{
    Red = 2,
    Green,
    Blue
};

TEST(TestOutput, output)
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::cout << "v: " << v << std::endl;

    std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}};
    std::cout << "m: " << m << std::endl;

    std::string s = "hello world";
    std::cout << "s: " << s << std::endl;

    std::map<std::map<int, std::string>, std::vector<int>> mm = {{{{1, "a"}, {2, "b"}}, {3}}};
    std::cout << "mm: " << mm << std::endl;
}

TEST(TestSfinae, enum)
{
    std::cout << "Color::RED: " << enum_name(Color::Red) << std::endl;
    std::cout << "Color::GREEN: " << enum_name(Color::Green) << std::endl;
    std::cout << "Color::BLUE: " << enum_name(Color::Blue) << std::endl;

    std::cout << "Color::Blue: " << enum_from_name<Color>("Blue") << std::endl;
}

struct A
{
};

struct B
{
    void test();
    int compare(const B &other) const;
};

template <typename T> struct C
{
    template <typename U> auto test(U) -> decltype(std::declval<T>()(std::declval<U>()), std::true_type())
    {
        std::cout << "Test(1)" << std::endl;
        return std::true_type();
    }

    template <typename U> std::false_type test(...)
    {
        std::cout << "Test(2)" << std::endl;
        return std::false_type();
    }
};

TEST(TestIsValid, is_valid)
{
    auto has_test = is_valid([](auto &&x) -> decltype(x.test()) {});
    EXPECT_FALSE(has_test(A()));
    EXPECT_TRUE(has_test(B()));

    auto has_compare = is_valid([](auto &&x) -> decltype(x.compare(B())) {});
    EXPECT_FALSE(has_compare(A()));
    EXPECT_TRUE(has_compare(B()));

    // 测试 sfinae
    C<decltype([](auto &&x) -> decltype(x.test()) { return x.test(); })> c;
    c.test<B>(B());
    c.test<int>(int());
}

struct Compare
{
    using has_abs = decltype([](auto &&x) -> decltype(std::abs(x)) {});
    using has_distance = decltype([](auto &&x, auto &&y) -> decltype(x.distance(y)) {});

    template <typename U>
    constexpr auto test(const U &x) -> decltype(std::declval<has_abs>()(std::declval<U>()), bool{})
    {
        std::cout << "has_abs" << std::endl;
        return true;
    }

    template <typename U> constexpr bool test(...)
    {
        std::cout << "no_abs" << std::endl;
        return false;
    }
};

TEST(TestCompare, compare)
{
    Compare cmp;
    // cmp.test(int(1));

    cmp.test(int(1));
    cmp.test<std::string>("hi");
}